package cn.tedu.charging.cost.service.impl;

import cn.tedu.charging.common.pojo.param.ChargingProcessParam;
import cn.tedu.charging.common.pojo.vo.ChargingProcessVO;
import cn.tedu.charging.cost.dao.repository.ChargingDataESRepository;
import cn.tedu.charging.cost.dao.repository.CostRuleFromRedisRepository;
import cn.tedu.charging.cost.dao.repository.CostRuleRepository;
import cn.tedu.charging.cost.pojo.po.ChargingDataPO;
import cn.tedu.charging.cost.pojo.po.CostResultPO;
import cn.tedu.charging.cost.pojo.vo.ChargingData;
import cn.tedu.charging.cost.service.CostService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.*;


@Slf4j
@Service
public class CostServicesImpl implements CostService {
//    定义全局变量
    // private Map<String, ChargingData>  chargingDataMap=new HashMap<>();
    @Autowired
    ChargingDataESRepository  chargingDataESRepository;





    @Override
    public ChargingProcessVO calculateCost(ChargingProcessParam chargingProcessParam) {
//       ChargingData chargingData = getChargingDataByOrderNo(chargingProcessParam.getOrderNo());
        //把从本地map中获取充电数据修改为 从ES 获取充电数据
        ChargingDataPO chargingData = getChargingDataByOrderNoFromES(chargingProcessParam.getOrderNo(), chargingProcessParam.getUserId(),chargingProcessParam.getGunId());

        log.debug("通过订单号:{}," +
                        "获取充电数据:{}," +
                        "当前同步次数:{}",
                chargingProcessParam.getOrderNo(),
                chargingData,
                chargingData.getCount());

        Integer currentHour = getCurrentHour();
        log.debug("当前时间小时:{}",currentHour);

        CostResultPO costResultPO=matchCostRule(chargingProcessParam.getStationId(),chargingProcessParam.getGunId(),currentHour);
        log.debug("通过枪类型：{},时间：{},获取计价规则：{}",chargingProcessParam.getGunId(),currentHour,costResultPO);
        if (costResultPO!=null){
//            获取计价规则名称
                String name = costResultPO.getName();
//                获取设备同步的充电度数
                Float chargingCapacity= chargingProcessParam.getChargingCapacity();
//                获取计价规则的每度电的价格
            BigDecimal powerFee = costResultPO.getPowerFee();
//            计算价格 充电度数*每度电的价格
//            BigDecimal cost = getCost(chargingCapacity, powerFee);
//            判断是否是第一次同步数据
//            判断的原因
//            第一次同步数据 可以直接 充电度数*每度电的价格
//            第N次同步数据 需要 充电度数-上一次同步度数 =计算真实充电度数   再计算价格
//            if (chargingData.getCount()==1){  第一种写法
            if (chargingData.isFirst()){
//                第一次同步数据
                firstCost2ChargingData(chargingCapacity,chargingData,currentHour,name,powerFee);
            }else{
//                第N次同步数据
                lastCost2ChargingData(chargingCapacity,chargingData,currentHour,name,powerFee);
            }


        }else {
            log.error("故障，计价规则为空，要进行系统告警，业务无法继续执行");
        }

// 1 本地map4个问题的解决    ES
        // 2 计价服务Vo 没有完成
        // 3 计价规则会高频的从mysql查询   缓存Redis
        // 4 订单服务调用计价后的处理逻辑
        ChargingProcessVO chargingProcessVO = buildChargingProcessVO(chargingData);


        return chargingProcessVO;
    }


    @Autowired
    CostRuleRepository  costRuleRepository;
    @Autowired
    CostRuleFromRedisRepository costRuleFromRedisRepository;
    /*
    * 通过厂站获取计价规则
    * */
    private List<CostResultPO> getCostRuleByStation(Integer stationId){
        log.debug("通过厂站ID:{},获取对应的计价规则",stationId);
        List<CostResultPO> costRulesFromRedis=costRuleFromRedisRepository.getCostRuleByStationId(stationId);
        if (!CollectionUtils.isEmpty(costRulesFromRedis)){
            log.debug("从redis能获取到相关的计价规则{}",costRulesFromRedis);
            return costRulesFromRedis;
        }else {
            log.debug("redis里面没有计价规则，从数据库获取计价规则");

            List<CostResultPO> costRules=costRuleRepository.getCostRuleByStationId(stationId);
            if (!CollectionUtils.isEmpty(costRules)){
                log.debug("从数据库获取到相关的计价规则{},并将回去的计价规则保存到redis数据库",costRules);
                costRuleFromRedisRepository.setCostRuleByStationId(stationId,costRules);
                return costRules;

            }

        }
        return Collections.emptyList();



    }
/*获取当前时间 小时*/
    private Integer getCurrentHour(){
        LocalDateTime now = LocalDateTime.now();
        return now.getHour();
    }

    /*
    * 匹配计价规则
    * */
    private CostResultPO matchCostRule(Integer stationId,Integer gunType,Integer hour){
        Long start=System.currentTimeMillis();
        List<CostResultPO> costRules=getCostRuleByStation(stationId);
        Long end=System.currentTimeMillis();
        log.debug("从数据库获取的计价规则耗时:{}",end-start);
        if (!CollectionUtils.isEmpty(costRules)){
            for (CostResultPO costRule : costRules) {
                Integer costRuleGunType = costRule.getGunType();
                if (costRuleGunType.equals(gunType)){
                    Integer startTime = costRule.getStartTime();
                    Integer endTime = costRule.getEndTime();
                    if (hour>=startTime && hour<endTime){
                        return costRule;
                    }
                }
            }
        }
        return null;
    }


//   充电费用计算
    private BigDecimal getCost(Float chargingCapacity,BigDecimal powerFee){
        //通过构造器 进行类型转换
        //Float 转换为 BigDecimal
        //BigDecimal bigDecimal = new BigDecimal(chargingCapacity);
        //基本类型的数字转换为 包装类型的 Integer
        //Integer i = new Integer(1);
        //String 转换为 包装类型的 String
        //String s = new String("1");
        //通过方法 进行转换 valueOf
        //通过方法 valueOf 转换 float 转为 String
        //String s = String.valueOf(chargingCapacity);
        //Float 转换为 BigDecimal
        BigDecimal bigDecimal = BigDecimal.valueOf(chargingCapacity);
        BigDecimal multiply=powerFee.multiply(bigDecimal);
        return multiply;
    }

//    获取订单号及其充电数据
//        private  ChargingData getChargingDataByOrderNo(String orderNo){
//    //        本地变量不行每次都需要创建个，应用全局变量
////            ChargingData chargingData = chargingDataMap.get(orderNo);
//            ChargingDataPO chargingData = chargingDataMap.get(orderNo);
//
//            if (chargingData==null){
//    //            表示该订单号是第一次创建充电数据
//                chargingData = new ChargingData();
//                chargingData.setCount(1);
//                chargingDataMap.put(orderNo,chargingData);
////                chargingDataESRepository.save(chargingData);
//            }else {
//    //            第N次
//    //            取上一次数据
//                Integer count=chargingData.getCount();
//                count=count+1;
//                chargingData.setCount(count);
//    //            todo ??
//
//
//
//
//            }
//            return chargingData;
//
//        }


//    从ES中获取充电数据
    private  ChargingDataPO getChargingDataByOrderNoFromES(String orderNo,Integer userId,Integer gunId){
        Optional<ChargingDataPO> optional=chargingDataESRepository.findById(orderNo);
        //判断 optional 是否有数据 value != null;
        if (optional.isPresent()){  //不为空
            ChargingDataPO chargingDataPO = optional.get();
            Integer count=chargingDataPO.getCount();
            count=count+1;
            chargingDataPO.setCount(count);

            //修改后的数据保存到ES
            log.debug("第N次同步充电数据，初始化充电数据，并且保存到ES:{}",chargingDataPO);
            chargingDataESRepository.save(chargingDataPO);
            log.debug("第N次同步充电数据，初始化充电数据，并且保存到ES 成功:{}",chargingDataPO);
            return chargingDataPO;

        }else{  //第一次记录   //从ES没有查询到充电数据 表示第一次同步
            //初始化一个充电数据
            ChargingDataPO chargingDataPO=new ChargingDataPO();
            chargingDataPO.setOrderNo(orderNo);
            chargingDataPO.setCount(1);
            chargingDataPO.setId(orderNo);
            chargingDataPO.setGunId(gunId);
            chargingDataPO.setUserId(userId);
            chargingDataPO.setStartTime(System.currentTimeMillis());

            //修改后的数据保存到ES
            log.debug("第一次同步充电数据，初始化充电数据，并且保存到ES:{}",chargingDataPO);
            chargingDataESRepository.save(chargingDataPO);
            log.debug("第一次同步充电数据，初始化充电数据，并且保存到ES 成功:{}",chargingDataPO);
            return chargingDataPO;

        }

    }

        private void firstCost2ChargingData(Float chargingCapacity, ChargingDataPO chargingData,Integer currentHour,String name,BigDecimal powerFee){
                log.debug("第一次同步数据，计算价格");
        //        计算价格 充电度数*每度电的价格
                BigDecimal cost = getCost(chargingCapacity, powerFee);
                log.debug("当前时间:{},计价规则的名称:{}," +
                                "充电度数:{},电费单价:{}," +
                                "本次花费:{},总花费:{}",
                        currentHour,name,
                        chargingCapacity,powerFee,
                        cost,cost);
        //        保存当前充电度数到充电数据，为下次  <<计算真实充电度数做准备》》
            chargingData.setChargingCapacity(chargingCapacity);

        //    保存当前花费到充电数据 ，为下次《《计算真实花费   做准备
                chargingData.setTotal(cost);
            log.debug("第一次同步充电数据，计算价格后，并且保存到ES:{}",chargingData);
            chargingDataESRepository.save(chargingData);
            log.debug("第一次同步充电数据，计算价格后，并且保存到ES成功{}",chargingData);




        }

        private  void lastCost2ChargingData(Float chargingCapacity,ChargingDataPO chargingData,Integer currentHour,String name,BigDecimal powerFee){
            log.debug("不是第一次同步数据，计算价格");
//            上次同步的充电度数
//            Float lastChargingCapacity = chargingData.getChargingCapacity(); //原始
//            当前实际的充电度数= 本次同步的充电度数-上次同步的充电度数
//            Float realChargingCapacity = chargingCapacity - lastChargingCapacity; //原始
            Float realChargingCapacity=chargingData.getChargingCapacity(chargingCapacity);
//            计算价格 = 本次实际充电度数*每度电的价格
//            BigDecimal cost=getCost(realChargingCapacity,powerFee); //原始写法
            BigDecimal currentCost=getCost(realChargingCapacity,powerFee);
            chargingData.setTotal(currentCost);
//            上次的花费
//            BigDecimal lastCost=chargingData.getTotal();   //原始写法
////            当前总花费
//            BigDecimal total=lastCost.add(cost);   //  原始写法


            log.debug("当前时间:{},计价规则的名称:{}," +
                            "充电度数:{},电费单价:{}," +
                            "本次花费:{},总花费:{}",
                    currentHour,name,
                    chargingCapacity,powerFee,
//                    cost,total); //原始写法
                    currentCost,chargingData.getTotal());
            //        保存当前充电度数到充电数据，为下次  <<计算真实充电度数做准备》》
            chargingData.setChargingCapacity(chargingCapacity);

            //    保存当前花费到充电数据 ，为下次《《计算真实花费   做准备
//            chargingData.setTotal(total);   //1原始写法

            log.debug("第N次同步充电数据，计算价格后，并且保存到ES:{}",chargingData);
            chargingDataESRepository.save(chargingData);
            log.debug("第N次同步充电数据，计算价格后，并且保存到ES成功{}",chargingData);


        }

/**
 * 通过充电数据 构建 计价服务计算价格的出参
 * @param chargingData
 * @return
 */

private ChargingProcessVO buildChargingProcessVO(ChargingDataPO chargingData){
    ChargingProcessVO chargingProcessVO=new ChargingProcessVO();
    BeanUtils.copyProperties(chargingData,chargingProcessVO);
//    充电时长计算
    Long startTime = chargingData.getStartTime();
    Long nowTime = System.currentTimeMillis();
//    消耗的时间
    Long costTime = nowTime - startTime;
    //如何计算已经充了几个小时呢？
    //Date 不推荐使用  --> joda-time --> java.time LocalDateTime
    //LocalDateTime now  = LocalDateTime.now();
    //Duration between = Duration.between(, now);
    //between.toHours();//计算已经充了多少小时
    chargingProcessVO.setTotalTime(costTime);
    return chargingProcessVO;
}






}
